package first.group.ssh.hostkey;

import first.group.ssh.packet.reader.StringReader;
import first.group.ssh.packet.writer.StringWriter;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.SignatureException;
import java.util.Arrays;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;

public final class SshEd25519 extends HostKeyAlgorithm {

    @Override
    public String getName() {
        return "ssh-ed25519";
    }

    public static class SshEd25519Key extends HostKeyAlgorithm.HostKey<SshEd25519> {
        public final byte[] key;

        public SshEd25519Key(byte[] key) {
            this.key = key;
        }

        @Override
        public SshEd25519 getAlgorithm() {
            return new SshEd25519();
        }

        @Override
        public boolean check(byte[] data, HostKeyAlgorithm.HostSignature<? extends HostKeyAlgorithm> signature) {
            if (!(signature instanceof SshEd25519Signature ed25519Signature)) {
                throw new IllegalArgumentException("Check signature of " + signature.getAlgorithm().getName() + " with key of " + getAlgorithm().getName());
            }
            var spec = EdDSANamedCurveTable.ED_25519_CURVE_SPEC;
            var publicKey = new EdDSAPublicKey(new EdDSAPublicKeySpec(key, spec));
            var verifier = new EdDSAEngine();
            try {
                verifier.initVerify(publicKey);
                verifier.update(data);
                return verifier.verify(ed25519Signature.sign);
            } catch (InvalidKeyException | SignatureException ex) {
                throw new RuntimeException(ex);
            }
        }

        @Override
        public void write(ByteBuffer buffer) {
            var posStart = buffer.position();
            buffer.position(buffer.position() + 4);
            
            var dataStart = buffer.position();
            StringWriter.writeString(buffer, getAlgorithm().getName());
            StringWriter.writeByteArray(buffer, key);
            
            var dataEnd = buffer.position();
            buffer.position(posStart);
            buffer.putInt(dataEnd - dataStart);
            buffer.position(dataEnd);
        }

        @Override
        public String toString() {
            return "SshEd25519Key{" + "key=" + key + '}';
        }
    }

    private static class SshEd25519KeyParser extends HostKeyAlgorithm.HostKeyParser<SshEd25519> {

        @Override
        public HostKeyAlgorithm.HostKey<SshEd25519> parse(ByteBuffer buffer) {
            var key = StringReader.readByteArray(buffer);
            if (key == null) {
                return null;
            }
            return new SshEd25519Key(key);
        }
    }
    
    private static final SshEd25519KeyParser keyParser = new SshEd25519KeyParser();

    @Override
    public HostKeyAlgorithm.HostKeyParser<SshEd25519> getKeyParser() {
        return keyParser;
    }

    public static class SshEd25519Signature extends HostKeyAlgorithm.HostSignature<SshEd25519> {
        public final byte[] sign;

        public SshEd25519Signature(byte[] sign) {
            this.sign = sign;
        }

        @Override
        public SshEd25519 getAlgorithm() {
            return new SshEd25519();
        }

        @Override
        public String toString() {
            return "SshEd25519Signature{" + "sign=" + Arrays.toString(sign) + '}';
        }
    }

    private static class SshEd25519SignatureParser extends HostKeyAlgorithm.HostSignatureParser<SshEd25519> {

        @Override
        public HostKeyAlgorithm.HostSignature<SshEd25519> parse(ByteBuffer buffer) {
            var sign = StringReader.readByteArray(buffer);
            if (sign == null) {
                return null;
            }
            return new SshEd25519Signature(sign);
        }
    }
    
    private static final SshEd25519SignatureParser signatureParser = new SshEd25519SignatureParser();

    @Override
    public HostKeyAlgorithm.HostSignatureParser<SshEd25519> getSignatureParser() {
        return signatureParser;
    }
}
